from .models import User, Movie, Rating
from restore.restore import data_restore
import pandas as pd
import numpy as np
import random
import datetime


#获取热门数据和各类随机电影
def get_top_data():
    with open('top/All_top') as f:
        top_ids = f.readlines()
    movies = []
    top_ids = list(set(top_ids))
    for id in top_ids:
        movie = Movie.objects.get(id=int(id))
        movie = {
            'id': movie.id,
            'title': movie.title,
            'url': movie.imdb_url,
            'poster_url': movie.poster_url
        }
        movies.append(movie)
    genre_list = ['Action', 'Adventure', 'Animation', 'Childrens', 'Comedy', 'Crime', 'Documentary', 'Drama', 'Fantasy',
                  'Film-Noir', 'Horror', 'Musical', 'Mystery', 'Romance', 'Sci-Fi', 'Thriller', 'War', 'Western']
    genre_cn_list = ['动作', '冒险', '动画', '儿童', '喜剧', '犯罪', '纪录', '戏剧', '玄幻',
                     '黑色', '恐怖', '音乐', '神秘', '爱情', '科学', '惊险', '战争', '西部']
    genre_dict = [{'En': En,
                   'Cn': Cn} for En, Cn in zip(genre_list, genre_cn_list)]
    genre_all = {
        genre: [] for genre in genre_list
    } #获取各种电影随机10-20个电影数据
    for genre in genre_list:
        genre_movies = []
        with open('top/'+genre+'_top') as f:
            top_ids = f.readlines()
        rand_ids = random.sample(top_ids, 10)
        for id in rand_ids:
            movie = Movie.objects.get(id=id)
            movie = {
                'id': movie.id,
                'title': movie.title,
                'url': movie.imdb_url,
                'poster_url': movie.poster_url
            }
            genre_movies.append(movie)
        genre_all[genre] = genre_movies

    return movies, genre_dict, genre_all


def train_data_process():
    pass


#判断是否已经评分过
def is_scored(user_id, movie_id):
    rating = Rating.objects.filter(user_id_id=user_id, movie_id_id=movie_id)
    if rating.exists():
        return rating[0].rating
    else:
        return False


#判断是否是新用户
def is_new(user_id):
    user = User.objects.get(id=user_id)
    if user.is_newcomer:
        return True
    else:
        return False


#用户评分
def score_user(user_id, movie_id, rating):
    user = User.objects.get(id=user_id)
    movie = Movie.objects.get(id=movie_id)
    rating = Rating(rating=rating)
    rating.user_id = user
    rating.movie_id = movie
    rating.save()


#取出用户评分数据
def take_rating(user_id):
    movie_ids = Rating.objects.filter(user_id=user_id).values('movie_id', 'rating')
    movie_ids = [[i['movie_id'], i['rating']] for i in movie_ids]
    rating_range = [i+1 for i in range(4, -1, -1)]
    rating_list = {i: [] for i in rating_range}
    for id in movie_ids:
        movie = Movie.objects.get(id=id[0])
        movie = {
            'id': movie.id,
            'title': movie.title,
            'url': movie.imdb_url,
            'poster_url': movie.poster_url,
            'rating': id[1],
        }
        rating_list[id[1]].append(movie)
    return rating_list, rating_range


#取出所有的电影数据
def take_all():
    movies = Movie.objects.all()
    movies_list = []
    for movie in movies:
        movie = {
            'id': movie.id,
            'title': movie.title,
            'url': movie.imdb_url,
            'poster_url': movie.poster_url
        }
        movies_list.append(movie)
    return movies_list


#gcmc方式推荐
def prediction_gcmc(user_id):
    outputs, v_dict = data_restore(user_id=user_id)
    outputs_idx_dict = {idx: id for id, idx in v_dict.items()}
    outputs = np.argsort(-outputs)
    movie_ids = Rating.objects.filter(user_id=user_id).values('movie_id')
    movie_ids = [i['movie_id'] for i in movie_ids]
    id_data = []
    for output in outputs:
        if outputs_idx_dict[output] not in movie_ids:
            id_data.append(outputs_idx_dict[output])
        if len(id_data) == 10:
            break
    movies = []
    for id in id_data:
        movie = Movie.objects.get(id=id)
        movie = {
            'id': movie.id,
            'title': movie.title,
            'url': movie.imdb_url,
            'poster_url': movie.poster_url
        }
        movies.append(movie)

    return movies


def prediction_sim(user_id):
    users_df = pd.DataFrame(User.objects.filter(is_staff=False, is_newcomer=False).values('id', 'gender', 'date_born', 'occupation'))
    user = pd.DataFrame(User.objects.filter(id=user_id).values('id', 'gender', 'date_born', 'occupation'))
    users_df = users_df.append(user, ignore_index=True)
    users_df['date_born'] = users_df['date_born'].map(lambda date: int(datetime.date.today().year - date.year))
    users_df.rename(columns={'date_born': 'age'}, inplace=True)
    occupation = ['administrator', 'artist', 'doctor', 'educator', 'engineer', 'entertainment', 'executive',
                  'healthcare', 'homemaker', 'lawyer', 'librarian', 'marketing', 'programmer', 'retired',
                  'salesman', 'scientist', 'student', 'technician', 'writer', 'other', 'none']
    age = users_df['age'].values
    age_max = age.max()
    occupation_dict = {f: i for i, f in enumerate(occupation, start=2)}
    u_dict = {j: i for i, j in enumerate(users_df['id'])}
    num_feats = 2 + len(occupation_dict)
    u_features = np.zeros((len(users_df), num_feats), dtype=np.float32)
    for _, row in users_df.iterrows():
        u_id = row['id']
        if u_id in u_dict.keys():
            u_features[u_dict[u_id], 0] = row['age'] / np.float(age_max)
            u_features[u_dict[u_id], 1] = np.float(row['gender'])
            u_features[u_dict[u_id], occupation_dict[row['occupation']]] = 1.
    user = u_features[-1]
    u_features = np.delete(u_features, -1, axis=0)
    user = np.mat(user)
    sim_list = []
    for feature in u_features:
        feature = np.mat(feature)
        cos = (user*feature.T)/(np.linalg.norm(user)*np.linalg.norm(feature))
        sim = 0.5+0.5*cos
        sim_list.append(sim.tolist()[0][0])
    sims = np.array(sim_list)
    u_dict_ = {j: i for i, j in u_dict.items()}
    min_idx = sims.argsort()[0:4]
    movie_ids = []
    for id in min_idx:
        ratings = Rating.objects.exclude(user_id_id=user_id).filter(user_id_id=u_dict_[id], rating__gt=3)
        for rating in ratings:
            movie_ids.append(rating.movie_id_id)
    movie_ids = list(set(movie_ids))
    try:
        movie_ids = random.sample(movie_ids, 10)
    except:
        pass
    movies = []
    for id in movie_ids:
        movie = Movie.objects.get(id=id)
        movie = {
            'id': movie.id,
            'title': movie.title,
            'url': movie.imdb_url,
            'poster_url': movie.poster_url,
        }
        movies.append(movie)
    return movies


def prediction_cold_user(user_id):
    user = User.objects.get(id=user_id)
    date = user.date_born
    year_max = datetime.date(year=date.year+2, month=date.month, day=date.day)
    year_min = datetime.date(year=date.year-2, month=date.month, day=date.day)
    movie_ids = Rating.objects.exclude(user_id=user).filter(rating=5, user_id__occupation=user.occupation,
                                                            user_id__gender=user.gender,
                                                            user_id__date_born__range=(year_min, year_max)).values('movie_id')
    movie_ids = [i['movie_id'] for i in movie_ids]
    scored_ids = Rating.objects.filter(user_id=user).values('movie_id')
    scored_ids = [i['movie_id'] for i in scored_ids]
    movie_ids = list(set(movie_ids)-set(scored_ids))
    length = len(movie_ids)
    rand_num = 10 #随机取推荐数
    try:
        if length >= rand_num: #防止达不到数量
            movie_ids = random.sample(movie_ids, rand_num)
        else: #达不到继续缩小特征范围
            movie_ids = Rating.objects.exclude(user_id=user).filter(rating=5, user_id__gender=user.gender,
                                              user_id__date_born__range=(year_min, year_max)).values('movie_id')
            movie_ids = [i['movie_id'] for i in movie_ids]
            movie_ids = list(set(movie_ids) - set(scored_ids))
            movie_ids = random.sample(movie_ids, rand_num)
    except Exception: #都达不到则继续缩小
            movie_ids = Rating.objects.exclude(user_id=user).filter(rating=5,
                                                                    user_id__gender=user.gender,).values('movie_id')
            movie_ids = [i['movie_id'] for i in movie_ids]
            movie_ids = list(set(movie_ids)-set(scored_ids))
            movie_ids = random.sample(movie_ids, rand_num)
    movies = []
    for id in movie_ids:
        movie = Movie.objects.get(id=id)
        movie = {
            'id': movie.id,
            'title': movie.title,
            'url': movie.imdb_url,
            'poster_url': movie.poster_url,
        }
        movies.append(movie)

    return movies
